import requests
import json
import jwt
import time
import os
from urllib.parse import urlencode
from .symbols import symbols, symbol_ids
from ..exceptions import PlatformAPIException, PlatformRequestException, PlatformNotSupportSymbolException, PlatformHighRateException
from ..restapibase import RestAPIBase


class QuoineRESTAPI(RestAPIBase):
    """
        document url: https://developers.quoine.com/
        before use, run _currency_pair() to create currency_pair.py
    """

    def __init__(self, api_key, secret_key, isdebug=False):
        self.api_key = api_key
        self.api_secret = secret_key
        self.isdebug = isdebug
        self.url = 'https://api.quoine.com'

    def _debug(self, result):
        if self.isdebug:
            print(result)

    def _get_auth(self, path):

        payload = {
            'path': path,
            'nonce': str(int(time.time())),
            'token_id': self.api_key
        }

        auth = jwt.encode(payload, self.api_secret, algorithm='HS256')

        headers = {
            'X-Quoine-API-Version': '2',
            'X-Quoine-Auth': auth,
            'Content-Type': 'application/json'
        }
        self._debug(headers)
        return headers

    def _create_path(self, path, data):
        query_string = '?{}'.format(urlencode(data))
        return '{}{}'.format(path, query_string)

    def _get(self, path, params={}):
        url = self.url + path
        if params:
            path = self._create_path(path, params)
        headers = self._get_auth(path)
        response = requests.get(url, headers=headers, params=params)

        self._debug('GET response: ' + str(response.text))

        errmsg = 'StatusCode: GET %s \nURL:%s \nParams: %s \nResponse: %s' % (
            response.status_code, url, str(params), response.text)
        rescode = response.status_code
        response = response.json()

        if rescode == 429:
            raise PlatformHighRateException(errmsg)

        if response:
            if hasattr(response, 'errors') or hasattr(response, 'message'):
                raise PlatformAPIException(errmsg)
            else:
                return response
        else:
            raise PlatformRequestException(errmsg)

    def _put(self, path, params={}):
        url = self.url + path
        if params:
            path = self._create_path(path, params)
        headers = self._get_auth(path)
        response = requests.put(url, headers=headers, params=params)

        self._debug('PUT response: ' + str(response.text))

        errmsg = 'StatusCode: PUT %s \nURL:%s \params: %s \nResponse: %s' % (
            response.status_code, url, str(params), response.text)
        rescode = response.status_code
        response = response.json()

        if rescode == 429:
            raise PlatformHighRateException(errmsg)

        if response:
            if 'errors' in response.keys() or 'message' in response.keys():
                raise PlatformAPIException(errmsg)
            else:
                return response
        else:
            raise PlatformRequestException(errmsg)

    def _post(self, path, data={}):
        url = self.url + path
        headers = self._get_auth(path)
        response = requests.post(url, headers=headers, data=data)
        # self._debug(response)
        self._debug('POST response: ' + str(response.text))

        errmsg = 'StatusCode: POST %s \nURL:%s \nData: %s \nResponse: %s' % (
            response.status_code, url, str(data), response.text)
        response = response.json()

        self._debug('POST response: ' + str(hasattr(response, 'errors')))

        if response:
            if 'errors' in response.keys() or 'message' in response.keys():
                raise PlatformAPIException(errmsg)
            else:
                return response
        else:
            raise PlatformRequestException(errmsg)

    # EndPoints
    def _symbols(self):
        url = '/products'
        result = self._get(url)
        self._debug('symbols: \n' + json.dumps(result, indent=2))

        symbols = []
        symbol_ids = {}
        for product in result:
            symbols.append(product['currency_pair_code'])
            symbol_ids.update(
                {product['currency_pair_code']: product['id']})

        sw = open(os.path.dirname(
            os.path.realpath(__file__)) + '/symbols.py', 'w')
        sw.write('symbols = ' + str(symbols) + '\n')
        sw.write('symbol_ids = ' + json.dumps(symbol_ids))
        sw.close()

        self._debug('symbols: \n' + str(symbols))
        self._debug('symbol_ids: \n' + json.dumps(symbol_ids, indent=2))
        return result

    def _order(self, symbol='ETHBTC', side='buy', order_type='limit', amount='0.01', price='0.1'):
        '''
            type: limit, market
        '''
        product_id = symbol_ids[symbol]
        url = '/orders/'

        data = {
            'product_id': product_id,
            'order_type': order_type,
            'side': side,
            'quantity': amount,
            'price': price,
        }

        data = json.dumps({'order': data})
        result = self._post(url, data)
        return result

    # Public
    def get_currency(self, currency):
        return currency.upper()

    def get_symbol(self, commodity, base):
        result = commodity.upper() + base.upper()
        if not (result in symbols):
            errmsg = 'Not supported: %s' % result
            raise PlatformNotSupportSymbolException(errmsg)

        return result
    # NotImplementedError

    def get_ticker(self, symbol):
        raise NotImplementedError()

    def get_depth(self, symbol):
        product_id = symbol_ids[symbol]
        url = '/products/' + str(product_id) + '/price_levels'
        resjson = self._get(url)
        result = {}

        bids = [{'price': bid[0], 'amount': bid[1]}
                for bid in resjson['sell_price_levels']]
        asks = [{'price': ask[0], 'amount': ask[1]}
                for ask in resjson['buy_price_levels']]

        bids.reverse()
        asks.reverse()

        result = {'bids': bids, 'asks': asks}
        self._debug('get_products:' + str(product_id) +
                    '\n' + json.dumps(result, indent=2))
        return result

    # NotImplementedError
    def get_account(self):
        raise NotImplementedError()

    def get_balances(self, type='trade'):
        url = '/accounts/balance'
        resjson = self._get(url)
        result = {balance['currency']: balance['balance']
                  for balance in resjson}
        self._debug('get_balance:\n' + json.dumps(result, indent=2))
        return result

    def limit_buy(self, symbol, amount, price):
        resjson = self._order(symbol, 'buy', 'limit', amount, price)
        result = {"id": resjson["id"]}
        self._debug('limit_buy: \n' + json.dumps(result, indent=2))
        return result

    def limit_sell(self, symbol, amount, price):
        resjson = self._order(symbol, 'sell', 'limit', amount, price)
        result = {"id": resjson["id"]}
        self._debug('limit_sell: \n' + json.dumps(result, indent=2))
        return result

    def market_buy(self, symbol, amount, price):
        resjson = self._order(symbol, 'buy', 'market', amount, price)
        result = {"id": resjson["id"]}
        self._debug('market_buy: \n' + json.dumps(result, indent=2))
        return result

    def market_sell(self, symbol, amount, price):
        resjson = self._order(symbol, 'sell', 'market', amount, price)
        result = {"id": resjson["id"]}
        self._debug('market_sell: \n' + json.dumps(result, indent=2))
        return result

    def get_order(self, order_id):
        url = '/orders/' + order_id

        result = self._get(url)
        self._debug('[GET] get_order: \n' + json.dumps(result, indent=2))
        return result

    def get_all_orders(self):
        url = '/orders'

        params = {
            'with_details': 1
        }

        result = self._get(url, params)
        self._debug('[GET] get_orders: \n' + json.dumps(result, indent=2))
        return result

    def get_open_orders(self):
        url = '/orders'

        params = {
            'status': 'live',
            'with_details': 1
        }

        result = self._get(url, params)
        self._debug('[GET] get_orders: \n' + json.dumps(result, indent=2))
        return result

    def cancel_order(self, order_id):
        url = '/orders/' + order_id + '/cancel'

        result = self._put(url)
        self._debug('[PUT] del_order: \n' + json.dumps(result, indent=2))
        return result

    def cancel_all_orders(self):
        order_ids = []
        orders = self.get_open_orders()['models']
        for order in orders:
            order_ids.append(order['id'])

        result = True
        for order_id in order_ids:
            resjson = self.cancel_order(str(order_id))
            if resjson["status"] != 'cancelled':
                result = False

        return result
    # NotImplementedError

    def withdraw(self, currency, amount, address):
        raise NotImplementedError()
